package com.cxyd.modules.wms.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.cxyd.common.constant.Constant;
import com.cxyd.common.constant.Constants;
import com.cxyd.common.constant.enums.wms.*;
import com.cxyd.common.exception.RootException;
import com.cxyd.common.page.PageData;
import com.cxyd.common.service.impl.CrudServiceImpl;
import com.cxyd.modules.api.dto.request.ContainerCallRequest;
import com.cxyd.modules.api.dto.request.ContainerRequest;
import com.cxyd.modules.sys.service.SysUserService;
import com.cxyd.modules.wms.dao.ContainerDao;
import com.cxyd.modules.wms.dao.LocateDao;
import com.cxyd.modules.wms.dao.StationDao;
import com.cxyd.modules.wms.dao.StockDao;
import com.cxyd.modules.wms.dto.ContainerDTO;
import com.cxyd.modules.wms.dto.po.LocatePo;
import com.cxyd.modules.wms.dto.po.StockPo;
import com.cxyd.modules.wms.entity.*;
import com.cxyd.modules.wms.service.ContainerService;
import com.cxyd.modules.wms.service.LogWorkService;
import com.cxyd.modules.wms.service.PostWcsService;
import com.cxyd.modules.wms.service.TaskService;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestBody;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 容器信息
 *
 * @author g_Cycle gengruyi01@163.com
 * @since 1.0.0 2023-04-27
 */
@Service
public class ContainerServiceImpl extends CrudServiceImpl<ContainerDao, ContainerEntity, ContainerDTO> implements ContainerService {
    private static final Logger logger = LoggerFactory.getLogger(ContainerServiceImpl.class);

    private final SysUserService userService;
    private final ContainerDao containerDao;
    private final LocateDao locateDao;
    private final StockDao stockDao;
    private final TaskService taskService;
    private final PostWcsService postWcsService;
    private final LogWorkService logWorkService;
    private final StationDao stationDao;

    public ContainerServiceImpl(SysUserService userService, ContainerDao containerDao, LocateDao locateDao, StockDao stockDao, TaskService taskService, PostWcsService postWcsService, LogWorkService logWorkService, StationDao stationDao) {
        this.userService = userService;
        this.containerDao = containerDao;
        this.locateDao = locateDao;
        this.stockDao = stockDao;
        this.taskService = taskService;
        this.postWcsService = postWcsService;
        this.logWorkService = logWorkService;
        this.stationDao = stationDao;
    }

    @Override
    public PageData<ContainerDTO> page(Map<String, Object> params) {
        //1.查询容器信息
        final IPage<ContainerEntity> page = baseDao.selectViewPage(
                getPage(params, "cont_id", true),
                getWrapper(params)
        );

        return getPageData(page, ContainerDTO.class);
    }

    @Override
    public QueryWrapper<ContainerEntity> getWrapper(Map<String, Object> params) {
        String contNo = (String) params.get("contNo");
        String contType = (String) params.get("contType");

        QueryWrapper<ContainerEntity> wrapper = new QueryWrapper<>();
        wrapper.like(StringUtils.isNotBlank(contNo), "cont_no", contNo);
        wrapper.eq(StringUtils.isNotBlank(contType), "cont_type", contType);

        return wrapper;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(@RequestBody Long[] ids) {
        for (int i = 0; i < ids.length; i++) {
            deleteContById(ids[i]);
        }
    }

    public void deleteContById(Long id) {
        ContainerEntity entity = containerDao.getContById(id);
        if (entity != null) {
            throw new RootException("托盘已存在于库存中，不能删除！");
        }
        baseDao.deleteById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void register(ContainerRequest request) {
        String contNo = request.getContNo();
        Long userId = Constant.ClientCode.PDA.getId();
        ContainerEntity containerEntity = containerDao.selectOne(new QueryWrapper<ContainerEntity>().eq("cont_no", contNo));
        if (ObjectUtils.isEmpty(containerEntity) || containerEntity.getStatus() != StatusEnum.USABLE.getCode()) {
            throw new RootException("托盘[" + contNo + "]未在系统中维护信息!");
        }
        List<StockEntity> stockEntities = stockDao.selectList(new QueryWrapper<StockEntity>().eq("cont_no", contNo));
        if (!CollectionUtils.isEmpty(stockEntities)) {
            throw new RootException("托盘[" + contNo + "]存在库存!无法进行空框登记!");
        }
//        StationEntity stationEntity = stationDao.selectOne(new QueryWrapper<StationEntity>().eq("dvc_no", containerEntity.getContType())
//                .eq("station_type", "IN"));
//        if (ObjectUtils.isEmpty(stationEntity) || stationEntity.getStationSt() != StatusEnum.USABLE.getCode()) {
//            throw new RootException("没有维护此站台或当前站台不可用!");
//        }
        String stationNo = request.getStationNo();
        String dcvNo = containerEntity.getContType();
        //查找空闲可用的货位(按规则进行分配：层、列、排)
        List<LocateEntity> locateEntities = locateDao.selectList(new QueryWrapper<LocateEntity>()
                .eq("dvc_no", dcvNo)
                .eq("locate_st", LocateStEnum.FREE.getCode())
                .eq("active", StatusEnum.USABLE.getCode())
                .eq("locate_type", LocateTypeEnum.CP_HW.getCode())
                .orderByAsc("level_num", "col_num", "row_num"));
        if (CollectionUtils.isEmpty(locateEntities)) {
            throw new RootException("当前库区无可用空货位！");
        }
        Date startDate = new Date();
        String comment = "空托盘[" + contNo + "]登记";
        String code = "0";
        String msg = comment + "成功";
        String work = "ContainerApiController.register";
        LogWorkEntity insertLogWork = new LogWorkEntity();
        insertLogWork.setWorkName("空容器登记");
        insertLogWork.setRelateNo(contNo);
        insertLogWork.setWorkProc(work);
        insertLogWork.setCreator(userId);
        insertLogWork.setCreateDate(startDate);
        //货位预分配
        LocateEntity locateEntity = locateEntities.get(0);
        LocatePo updateLocate = new LocatePo();
        updateLocate.setLocateId(locateEntity.getLocateId());
        updateLocate.setLocateSt(LocateStEnum.PRE.getCode());
        updateLocate.setLocateStOld(LocateStEnum.FREE.getCode());
        updateLocate.setContNo(contNo);
        updateLocate.setUpdateDate(startDate);
        updateLocate.setUpdater(userId);
        updateLocate.setComments(comment);
        try {
            int locateUpdateNum = locateDao.occupy(updateLocate);
            if (locateUpdateNum <= 0) {
                throw new RootException("货位预占用失败！");
            }
        } catch (Exception e) {
            logger.info("货位预占用失败，货位号={}", locateEntity.getLocateNo(), e);
            code = "1001";
            msg = e.getMessage();
            insertLogWork.setWorkCode(code);
            insertLogWork.setWorkMsg(msg);
            logWorkService.logWorkInsert(insertLogWork);
            throw new RootException("货位预占用失败！");
        }
        //生成空托盘库存
        StockEntity insertStock = new StockEntity();
        insertStock.setContNo(contNo);
        insertStock.setCurPosition(stationNo);
        insertStock.setPositionType(locateEntity.getLocateType());
        insertStock.setSpuNo(Constants.EMPTY_SPU_NO);
        insertStock.setSpuName(Constants.EMPTY_SPU_NAME);
        insertStock.setStockSt(StatusEnum.DISABLE.getCode());
        insertStock.setLockSt(StatusEnum.USABLE.getCode());
        insertStock.setStockQty(BigDecimal.ONE);
        insertStock.setLockedQty(BigDecimal.ZERO);
        insertStock.setCreator(userId);
        insertStock.setCreateDate(startDate);
        insertStock.setUpdater(userId);
        insertStock.setUpdateDate(startDate);
        insertStock.setComments(comment);
        try {
            stockDao.insert(insertStock);
        } catch (Exception e) {
            logger.info("生成空托盘库存失败", e);
            code = "1001";
            msg = e.getMessage();
            insertLogWork.setWorkCode(code);
            insertLogWork.setWorkMsg(msg);
            logWorkService.logWorkInsert(insertLogWork);
            throw new RootException("生成空托盘库存失败！");
        }
        //生成空托盘任务
        TaskEntity insertTask = new TaskEntity();
        insertTask.setTaskType(TaskTypeEnum.WAREHOUSE_ENTRY.getCode());
        insertTask.setTaskSubType(TaskSubTypeEnum.EMPTY_PALLET_STORAGE.getCode());
        insertTask.setContNo(contNo);
        insertTask.setSpuNo(insertStock.getSpuNo());
        insertTask.setSpuName(insertStock.getSpuName());
        insertTask.setFromPosition(stationNo);
        insertTask.setToPosition(locateEntity.getLocateNo());
        insertTask.setCurrentPosition(stationNo);
        insertTask.setStockId(insertStock.getStockId());
        insertTask.setTaskSt(TaskStEnum.INITIAL.getCode());
        insertTask.setPlanQty(BigDecimal.ONE);
        insertTask.setActlQty(BigDecimal.ONE);
        insertTask.setCreator(userId);
        insertTask.setCreateDate(startDate);
        insertTask.setUpdater(userId);
        insertTask.setUpdateDate(startDate);
        insertTask.setComments(comment);
        try {
            taskService.taskInsert(insertTask);
        } catch (Exception e) {
            logger.info("生成空托盘任务失败", e);
            code = "1001";
            msg = e.getMessage();
            insertLogWork.setWorkCode(code);
            insertLogWork.setWorkMsg(msg);
            logWorkService.logWorkInsert(insertLogWork);
            throw new RootException("生成空托盘任务失败！");
        }
        //生成搬送任务
        PostWcsEntity insertPostWcs = new PostWcsEntity();
        insertPostWcs.setContNo(contNo);
        insertPostWcs.setContType(containerEntity.getContType());
        insertPostWcs.setTrkType(PostWcsTypeEnum.IN.getCode());
        insertPostWcs.setFrmPos(stationNo);
        insertPostWcs.setToPos(locateEntity.getLocateNo());
        insertPostWcs.setCurPos(stationNo);
        insertPostWcs.setTrkSt(PostWcsStEnum.INITIAL.getCode());
        insertPostWcs.setIsFull(PostWcsFullEnum.EMPTY.getCode());
        insertPostWcs.setCreator(userId);
        insertPostWcs.setCreateDate(startDate);
        insertPostWcs.setUpdater(userId);
        insertPostWcs.setUpdateDate(startDate);
        insertPostWcs.setComments(comment);
        try {
            postWcsService.postWcsInsert(insertPostWcs);
        } catch (Exception e) {
            logger.info("生成搬送任务失败", e);
            code = "1001";
            msg = e.getMessage();
            insertLogWork.setWorkCode(code);
            insertLogWork.setWorkMsg(msg);
            logWorkService.logWorkInsert(insertLogWork);
            throw new RootException("生成搬送任务失败！");
        }
        msg = "空容器[" + contNo + "]登记成功！目标位置[" + locateEntity.getLocateNo() + "]";
        insertLogWork.setWorkCode(code);
        insertLogWork.setWorkMsg(msg);
        logWorkService.logWorkInsert(insertLogWork);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void emptyContainerCall(ContainerCallRequest request) {
        int planQty = request.getPlanQty();
        Date startDate = new Date();
        Long userId = Constant.ClientCode.WCS.getId();
        String stationNo = request.getStationNo();
        StationEntity stationEntity = stationDao.selectOne(new QueryWrapper<StationEntity>().eq("station_no", stationNo));
        String dvcNo = stationEntity.getDvcNo();
        if (ObjectUtils.isEmpty(stationEntity) || stationEntity.getStationSt() != 0) {
            throw new RootException("站台[" + stationNo + "]没有维护或已被禁用!");
        }
//        request.setPositionType(LocateTypeEnum.HW.getCode());
//        request.setLockSt(StatusEnum.USABLE.getCode());
//        request.setStockSt(StatusEnum.USABLE.getCode());
//        request.setSpuNo(Constants.EMPTY_SPU_NO);
//        if (ContTypeEnum.HK.getCode().equals(dvcNo)) {
//            request.setContType("HW-01K");
//        } else if (ContTypeEnum.YF.getCode().equals(dvcNo)) {
//            request.setContType("HW-02K");
//        } else {
//            throw new RootException("站台[" + stationNo + "]对应的设备类型不正确!");
//        }

        List<StockEntity> stockEntities = stockDao.selectList(new QueryWrapper<StockEntity>().lambda()
                .like(StockEntity::getPositionType, LocateTypeEnum.HW.getCode())
                .eq(StockEntity::getLockSt, StatusEnum.USABLE.getCode())
                .eq(StockEntity::getStockSt, StatusEnum.USABLE.getCode())
                .eq(StockEntity::getSpuNo, Constants.EMPTY_SPU_NO));
        if (CollectionUtils.isEmpty(stockEntities) || stockEntities.size() < planQty) {
            throw new RootException("站台[" + stationNo + "]对应的库区没有足够的空托盘!");
        }
        String comment = "空托盘呼叫";
        String code = "0";
        String msg = "空托盘呼叫成功";
        String work = "ContainerApiController.emptyContainerCall";
        LogWorkEntity insertLogWork = new LogWorkEntity();
        insertLogWork.setWorkName("空容器呼叫");
        insertLogWork.setWorkProc(work);
        insertLogWork.setCreator(userId);
        insertLogWork.setCreateDate(startDate);
        int updateNum = 0;
        for (StockEntity stockPo : stockEntities) {
            if (updateNum >= planQty) {
                logger.info("空托盘呼叫完成");
                break;
            }
            //锁定库存
            StockPo updateStock = new StockPo();
            updateStock.setStockSt(StatusEnum.DISABLE.getCode());
            updateStock.setStockId(stockPo.getStockId());
            updateStock.setStockStOld(StatusEnum.USABLE.getCode());
            updateStock.setUpdateDate(startDate);
            updateStock.setUpdater(userId);
            updateStock.setLockSt(StatusEnum.USABLE.getCode());
            updateStock.setComments(comment);
            int i = stockDao.updateStockForSelect(updateStock);
            if (i <= 0) {
                logger.info("修改空托盘库存失败" + stockPo.getStockId());
                continue;
            }
            //生成呼叫任务
            TaskEntity insertTask = new TaskEntity();
            insertTask.setTaskType(TaskTypeEnum.OUTBOUND.getCode());
            insertTask.setTaskSubType(TaskSubTypeEnum.EMPTY_PALLET_OUTBOUND.getCode());
            insertTask.setContNo(stockPo.getContNo());
            insertTask.setSpuNo(stockPo.getSpuNo());
            insertTask.setSpuName(stockPo.getSpuName());
            insertTask.setFromPosition(stockPo.getCurPosition());
            insertTask.setToPosition(stationNo);
            insertTask.setCurrentPosition(stockPo.getCurPosition());
            insertTask.setStockId(stockPo.getStockId());
            insertTask.setTaskSt(TaskStEnum.INITIAL.getCode());
            insertTask.setCreator(userId);
            insertTask.setCreateDate(startDate);
            insertTask.setUpdater(userId);
            insertTask.setUpdateDate(startDate);
            insertTask.setComments(comment);
            insertTask.setPlanQty(BigDecimal.ONE);
            insertTask.setActlQty(BigDecimal.ONE);
            try {
                taskService.taskInsert(insertTask);
            } catch (Exception e) {
                logger.info("生成空托盘任务失败", e);
                code = "1001";
                msg = e.getMessage();
                insertLogWork.setRelateNo(stockPo.getContNo());
                insertLogWork.setWorkCode(code);
                insertLogWork.setWorkMsg(msg);
                logWorkService.logWorkInsert(insertLogWork);
                throw new RootException("生成空托盘任务失败！");
            }
            //生成搬送任务
            PostWcsEntity insertPostWcs = new PostWcsEntity();
            insertPostWcs.setContNo(stockPo.getContNo());
            insertPostWcs.setContType(dvcNo);
            insertPostWcs.setTrkType(PostWcsTypeEnum.OT.getCode());
            insertPostWcs.setFrmPos(stockPo.getCurPosition());
            insertPostWcs.setToPos(stationNo);
            insertPostWcs.setCurPos(stockPo.getCurPosition());
            insertPostWcs.setTrkSt(PostWcsStEnum.INITIAL.getCode());
            insertPostWcs.setIsFull(PostWcsFullEnum.EMPTY.getCode());
            insertPostWcs.setCreator(userId);
            insertPostWcs.setCreateDate(startDate);
            insertPostWcs.setUpdater(userId);
            insertPostWcs.setUpdateDate(startDate);
            insertPostWcs.setComments(comment);
            try {
                postWcsService.postWcsInsert(insertPostWcs);
            } catch (Exception e) {
                logger.info("生成搬送任务失败", e);
                code = "1001";
                msg = e.getMessage();
                insertLogWork.setRelateNo(stockPo.getContNo());
                insertLogWork.setWorkCode(code);
                insertLogWork.setWorkMsg(msg);
                logWorkService.logWorkInsert(insertLogWork);
                throw new RootException("生成搬送任务失败！");
            }
            updateNum++;
        }
        if (updateNum < planQty) {
            logger.info("呼叫托盘数量不足 应呼叫planQty={}, 实际呼叫={}", planQty, updateNum);
            //记录日志
            msg = "呼叫托盘数量不足！";
            insertLogWork.setWorkCode("1001");
            insertLogWork.setWorkMsg(msg);
            insertLogWork.setRelateNo(stationNo + "-" + planQty);
            logWorkService.logWorkInsert(insertLogWork);
            throw new RootException(msg);
        }
        //记录日志
        msg = "站台[" + request.getStationNo() + "]呼叫空托盘成功!需求数量[" + request.getPlanQty() + "]";
        insertLogWork.setWorkCode(code);
        insertLogWork.setWorkMsg(msg);
        insertLogWork.setRelateNo(stationNo + "-" + planQty);
        logWorkService.logWorkInsert(insertLogWork);
    }
}